package xm.view
{
	import com.greensock.TweenLite;
	import com.greensock.data.TweenLiteVars;
	import com.greensock.easing.Cubic;
	
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.Graphics;
	import flash.display.Sprite;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	
	import xm.components.SkinnableContainer;
	import xm.core.XMSprite;
	import xm.manager.ConfigManager;
	import xm.manager.StageManager;
	
	
	/**
	 * <br> 클래스 설명 : 모바일 어플리케이션의 서브페이지 - 좌우 페이지 이동가능 ( Carousel 방식 )</br>
	 * @author 김정환
	 */	
	public class ViewCarousel extends SkinnableContainer
	{
		
		//-----------------------------------------------------------------------------------
		// public vars
		//-----------------------------------------------------------------------------------
		
		
		
		//-----------------------------------------------------------------------------------
		// protected / internal vars
		//-----------------------------------------------------------------------------------
		
		/**
		 * 자식 리스트 저장소 
		 */		
		protected var childViewListRepository : Array = [];
		
		/**
		 * 자식 리스트 
		 */		
		protected var childViewList : Array = []
		
		/**
		 * 자식 비트맵 리스트
		 */		
		protected var childBMList : Array = [];
		
		/**
		 * 페이지 아이콘 리스트 
		 */		
		protected var pageIconList : Vector.<Sprite> = new Vector.<Sprite>();
		
		/**
		 * 현재 선택된 인덱스 
		 */		
		protected var selectedIndex : int = 0;
		
		/**
		 * 이전에 선택된 인덱스 
		 */		
		protected var prevIndex : int = 0;
		
		//-----------------------------------------------------------------------------------
		// private vars
		//-----------------------------------------------------------------------------------
		
		
		private const ICON_RADIUS : Number = 3; // 페이지 아이콘의 반지름
		private const ICON_COLOR : uint = 0xa6a6a6; // 페이지 아이콘의 색상
		private const ICON_DEFAULT_ALPHA : Number = 0.3; // 선택되지 않은 페이지 아이콘의 알파값
		private const ICON_GAP : Number = 10; // 페이지 아이콘과 아이콘 사이의 틈새 너비.
		
		
		
		
		
		// 값이 0.5 일때 화면의 절반이 움직이면 다음화면으로 간주한다.
		private const SENSITIVITY_FOR_FLICK : Number = 0.5; // 0~1 움직임을 감지하는 정도.
		
		// 민감한 움직임의 keyPoint
		private var _movePower : Number = 0; // 움직임의 세기정도를 플리킹에 반영하기위한 변수.
		
		private var _carouselPoint : Point = new Point(0,0);
		private var _onMove : Boolean = false;
		private var _onDown : Boolean = false;
		private var _isNew : Boolean = false;
		private var _downPoint : Point = new Point( 0,0 );
		// 페이지 아이콘 사용여부
		private var _usePageIcon : Boolean = true;
		//-----------------------------------------------------------------------------------
		// public functions
		//-----------------------------------------------------------------------------------
		
		
		/**
		 * constructor 
		 */	
		public function ViewCarousel()
		{
			super();
		}
		
		
		
		/**
		 * commitProperties
		 */		
		override public function commitProperties():void
		{
			super.commitProperties();
			
		}
		
		
		
		/**
		 * updateDisplayList
		 */		
		override public function updateDisplayList( enforce : Boolean = false ):void
		{
			super.updateDisplayList(enforce);
		}

		
		
		/**
		 * @param childViewList =  클래스 리스트로 넘김.
		 */		
		public function setChildView( childClassList : Array ):void
		{
			// 자식관련
			childViewListRepository = childClassList;
			childBMList[ 1 ] = getChildBMAt( selectedIndex );
			childBMList[ 2 ] = getChildBMAt( selectedIndex + 1 );
			
			addChild( childBMList[ 1 ] as Bitmap );
			if( childBMList[ 2 ] )
			{
				addChild( childBMList[ 2 ] as Bitmap );
				childBMList[ 2 ].x = childBMList[ 1 ].x + childBMList[ 1 ].width;
			}
			
			// 페이지 아이콘 관련.
			if( pageIconList.length > 0 ) resetPageIcon();
			if( _usePageIcon == true )
			{
				setPageIcon();
				iconIndexControl();
				iconPositionControl();
			}
		}
		
		
		
		/**
		 * <br>페이지 하단에 페이지 아이콘을 표시함.</br>
		 */	
		public function pageIconON():void
		{
			_usePageIcon = true;
			
			setPageIcon();
			iconIndexControl();
			iconPositionControl();
		}
		
		/**
		 * <br>페이지 하단에 페이지 아이콘을 꺼버림.</br>
		 */	
		public function pageIconOFF():void
		{
			_usePageIcon = false;
			resetPageIcon();
		}
		
		
		
		
		
		
		
		//-----------------------------------------------------------------------------------
		// life cycle functions
		//-----------------------------------------------------------------------------------
		
		/**
		 * 자식 생성하는 시점.
		 */		
		override public function createChildren():void
		{
			super.createChildren();
			
//			this.addEventListener(TouchEvent.TOUCH_BEGIN, onDown );
			this.addEventListener(MouseEvent.MOUSE_DOWN, onDown );
		}
		
		
		/**
		 * 생성완료 시점.
		 */		
		override public function creationComplete():void
		{
			super.creationComplete();
			
		}
		
		
		/**
		 * 부모가 생긴시점.
		 */		
		override public function add():void
		{
			super.add();
			
			addChildCurrentView();
		}
		
		
		/**
		 * 부모로부터 제거된 시점.
		 */		
		override public function remove():void
		{
			super.remove();
		}
		
		
		/**
		 * 메모리에서 지워지는 시점.
		 */		
		override public function destroy():void
		{
			super.destroy();
			
			var disObj : DisplayObject, 
				bm : Bitmap, 
				sp : Sprite,
				i:int,
				len:int,
				s : Stage = StageManager.getInstance().stage;
				
				
			// 현재 움직임을 모두 정지시킨다.
			for each( bm in childBMList )
			{
				if( bm != null )
				{
					TweenLite.killTweensOf( bm );
				}
			}
			
			// 이벤트 제거
			this.removeEventListener(MouseEvent.MOUSE_DOWN, onDown );
			s.removeEventListener(MouseEvent.MOUSE_MOVE, onMove );
			s.removeEventListener(MouseEvent.MOUSE_UP, onUp );
			
			
			// 속성제거
			selectedIndex = 0;
			prevIndex = 0;
			_movePower = NaN;
			_carouselPoint = null;
			_onMove = false;
			_onDown = false;
			_isNew = false;
			_downPoint = null;
			_usePageIcon = false;
			
			
			
			// 자식제거
			i=0;
			len=pageIconList.length;
			while( i<len )
			{
				sp = pageIconList[ i ];
				if( sp )
				{
					if( sp.parent ) sp.parent.removeChild( sp );
					pageIconList[ i ] = null;
					sp = null;
					delete pageIconList[ i ];
				}
				++i;
			}
			pageIconList = null;
			
			
			
			i=0;
			len = childBMList.length;
			while( i<len )
			{
				bm = childBMList[ i ] as Bitmap;
				if( bm )
				{
					if( bm.parent ) bm.parent.removeChild( bm );
					childBMList[ i ] = null;
					bm = null;
					delete childBMList[ i ];
				}
				++i;
			}
			childBMList = null;
			
			
			
			i=0;
			len = childViewList.length;
			while( i<len )
			{
				if( childViewList[ i ] is XMSprite )
				{
					( childViewList[ i ] as XMSprite ).destroy();
				}
				else
				{
					disObj = childViewList[ i ] as DisplayObject;
					if( disObj )
					{
						if( disObj.parent ) disObj.parent.removeChild( disObj );
					}
					disObj = null;
				}
				
				delete childViewList[ i ];
				++i;
			}
			childViewList = null;
			
			
			
			// 자식 뷰 타입 리스트제거.
			i = 0;
			len = childViewListRepository.length;
			while( i<len )
			{
				delete childViewListRepository[ i ];
				++i;
			}
			childViewListRepository = null;
			
		}
		
		//-----------------------------------------------------------------------------------
		// setter / getter
		//-----------------------------------------------------------------------------------
		
		/**
		 * <br>마우스 이벤트에 대한 반응여부 결정.</br>
		 * <br>외부에서 마우스 다운, 마우스 무브, 마우스 업등을 사용하기 위해선 잠시 false 로 해야합니다.</br>
		 * @param isUse 플리킹 사용여부.
		 */		
		public function set useCarousel( isUse : Boolean ):void
		{
			if( isUse )
			{
				this.addEventListener(MouseEvent.MOUSE_DOWN, onDown );
			}
			else
			{
				this.removeEventListener(MouseEvent.MOUSE_DOWN, onDown );
			}
		}
		
		
		//-----------------------------------------------------------------------------------
		// protected functions
		//-----------------------------------------------------------------------------------
		
		
		
		//-----------------------------------------------------------------------------------
		// internal functions
		//-----------------------------------------------------------------------------------
		
		
		
		
		//-----------------------------------------------------------------------------------
		// private functions
		//-----------------------------------------------------------------------------------
		
		// 실제 자식을 생성하거나, 생성되어있으면 플리킹 대상에 포함시키고,
		// 플리킹 대상의 
		private function makeChildAndBitmap():void
		{
			var len : int = childViewListRepository.length,
				bm : Bitmap;
			
			
			// 앞페이지로 이동된경우 
			if( prevIndex > selectedIndex )
			{
				// 2. 기존에 가장 우측에 있던 이미지는 한칸 더 우측으로 이동했으므로 제거함.
				bm = childBMList.pop() as Bitmap;
				
				if( bm != null && contains( bm ) == true )
				{
					removeChild( bm );
				}
				
				childBMList[ 2 ] = childBMList[ 1 ];
				childBMList[ 1 ] = childBMList[ 0 ];
				childBMList[ 0 ] = ( selectedIndex == 0 ) ? null : getChildBMAt( selectedIndex-1 );
				
				// 1. 좌측이미지로 등록된것을 selectedIndex 의 왼쪽에 붙이고,
				bm = childBMList[ 0 ] as Bitmap; 
				if( bm != null && contains( bm ) == false )
				{
					this.addChild( bm );
					bm.x = (childBMList[ 1 ] as Bitmap).x-bm.width;
				}
					
			}
			// 뒷페이지로 이동된경우
			else if( prevIndex < selectedIndex )
			{
				// 2. 기존에 가장 좌측에 있던 이미지는 한칸 더 좌측으로 이동했으므로 제거함.
				bm = childBMList[ 0 ] as Bitmap;
				
				if( bm != null && contains( bm ) == true )
				{
					removeChild( bm );
				}
				
				childBMList[ 0 ] = childBMList[ 1 ];
				childBMList[ 1 ] = childBMList[ 2 ];
				childBMList[ 2 ] = ( selectedIndex == len-1 ) ? null : getChildBMAt( selectedIndex+1 );
				
				// 1. 우측이미지로 등록된것을 selectedIndex 의 오른쪽에 붙이고,
				bm = childBMList[ 2 ] as Bitmap; 
				if( bm != null && contains( bm ) == false )
				{
					this.addChild( bm );
					bm.x = (childBMList[ 1 ] as Bitmap).x+(childBMList[ 1 ] as Bitmap).width;
				}
				
			}
		}		
		
		
		
		// 페이지 아이콘 셋팅
		private function setPageIcon():void
		{
			var i:int=0,
				len:int=childViewListRepository.length,
				icon:Sprite;
			
			while( i<len )
			{
				icon = getPageIcon();
				pageIconList.push( icon ); 
				addChild( icon );
				
				++i;
			}
		}		
		
		
		// 인덱스에 따른 자식 컨트롤
		private function indexControl():void
		{
			var s : Stage = StageManager.getInstance().stage,
				i : int = 0,
				len : int = childBMList.length,
				tweenVar : TweenLiteVars;
			
			
			while( i<len )
			{
				if( childBMList[ i ] != null )
				{
					tweenVar = new TweenLiteVars();
					tweenVar.prop( "x", s.stageWidth*+(i-1 + prevIndex-selectedIndex) );
					tweenVar.ease( Cubic.easeOut );
					
					// 완료 핸들러는 매번 실행할 필요가 없으므로 currentChild 일때만 실행.
					if( i==1 ) tweenVar.onComplete( addChildCurrentView ); 
					
					TweenLite.to( childBMList[ i ], 0.3, tweenVar );
				}
				++i;
			}
			
			if( _usePageIcon )
			{
				iconIndexControl();
			}
		}		
		
		
		
		// 자식이 생성되어 있으면 반환 없으면 생성하여 반환.
		private function getChildViewAt( idx : int ):DisplayObject
		{
			if( childViewList[ idx ] == null && childViewListRepository.length > idx ) 
			{
				childViewList[ idx ] = new childViewListRepository[ idx ]();
			}
			
			return childViewList[ idx ] as DisplayObject;
		}
		
		// 비트맵이 생성되어 있으면 반환 없으면 생성하여 반환.
		private function getChildBMAt( idx : int ):Bitmap
		{
			var disObj : DisplayObject = getChildViewAt( idx );
			if( disObj == null ) return null;
			
			var s : Stage = StageManager.getInstance().stage,
				bd : BitmapData;
			
			bd = new BitmapData( s.stageWidth, s.stageHeight );
			bd.draw( disObj );
			
			return new Bitmap( bd );
		}
		
		
		// 화면에 현재인덱스 차일드를 붙인다.
		private function addChildCurrentView():void
		{
			// 만약 다시 마우스 다운이 되었으면 addChild 는 잠시 보류함.
			if( _onDown == true )
			{
				return;
			}
			
			if( childViewList[ selectedIndex ] && 
				this.contains( childViewList[ selectedIndex ] ) == false )
			{
				addChild( childViewList[ selectedIndex ] );
			}
			
			// 움직임이 있었을 때에는 좌우셋팅함.
			if( _onMove )
			{
				// 움직임이 모두 끝났으니 좌우를 다시셋팅하고,
				makeChildAndBitmap();
			}
			
			iconsPosToHihgestLayer();
			_onMove = false;
			_isNew = false;
			
			
			
		}
		
		// 아이콘들을 최상위 레이어로 이동시킴.
		private function iconsPosToHihgestLayer():void
		{
			var icon : Sprite,
			maxIndex : int;
			
			maxIndex = numChildren - 1;
			// 페이지 아이콘을 최상단인덱스로 셋팅한다.
			for each( icon in pageIconList )
			{
				this.setChildIndex( icon , maxIndex );
			}
		}		
		
		// 페이지 아이콘 반환
		private function getPageIcon():Sprite
		{
			var sp : Sprite = new Sprite(),
				g : Graphics = sp.graphics;
			
			g.beginFill( ICON_COLOR, 1 );
			g.drawCircle(0,0,ICON_RADIUS);
			g.endFill();
			
			sp.alpha = ICON_DEFAULT_ALPHA;
			return sp;
		}
		
		
		
		// 아이콘 배치
		private function iconPositionControl():void
		{
			var len : int = pageIconList.length,
				i : int = 0,
				icon : Sprite,
				totalIconsWidth : Number = len*ICON_RADIUS + ( ( len-1 )*ICON_GAP ),
				s : Stage = StageManager.getInstance().stage,
				iconInitPosX : Number = Math.floor( (s.stageWidth-totalIconsWidth)/2 ),
				availHeight : Number = availableHeight;
			
			while( i<len )
			{
				icon = pageIconList[ i ] as Sprite;
				icon.x = iconInitPosX + i*ICON_RADIUS + i*ICON_GAP;
				icon.y = availHeight - 30;
				++i;
			}
		}
		
		// 인덱스 컨트롤
		private function iconIndexControl():void
		{
			var len : int = pageIconList.length,
				i : int = 0,
				icon : Sprite;
			
			
			while( i<len )
			{
				icon = pageIconList[ i ] as Sprite;
				
				if( i == selectedIndex )
				{
					icon.alpha = 1;
				}
				else
				{
					icon.alpha = ICON_DEFAULT_ALPHA;
				}
				
				++i;
			}
			
		}		
		
		// 페이지 아이콘들 전부 걷어내고 메모리 비움
		private function resetPageIcon():void
		{
			var len : int = pageIconList.length,
				i : int = 0,
				icon : Sprite;
			
			while( i<len )
			{
				icon = pageIconList.shift();
				if( this.contains( icon ) ) removeChild( icon );
				icon = null;
				
				++i;
			}
		}		
		
		
		//-----------------------------------------------------------------------------------
		// event handler
		//-----------------------------------------------------------------------------------
		
		
		/**
		 * @param e
		 */		
		protected function onDown(e:Event):void
		{
			_onDown = true;
			
			var s : Stage = StageManager.getInstance().stage,
				i:int=0,
				len:int=childViewList.length,
				bm : Bitmap;
			
			s.addEventListener(MouseEvent.MOUSE_MOVE, onMove );
			s.addEventListener(MouseEvent.MOUSE_UP, onUp );
//			s.addEventListener(TouchEvent.TOUCH_MOVE, onMove );
//			s.addEventListener(TouchEvent.TOUCH_END, onUp );
			
			_carouselPoint.x = s.mouseX;
			_carouselPoint.y = s.mouseY;
			_downPoint.x = _carouselPoint.x;
			_downPoint.y = _carouselPoint.y;
			
			// 현재 움직임을 모두 정지시킨다.
			for each( bm in childBMList )
			{
				if( bm != null )
				{
					TweenLite.killTweensOf( bm );
				}
			}
			
			// 이동중인데 또 이동을 시도하려 하면 우선 좌우셋팅을 다시한다.
			if( _onMove )
			{
				makeChildAndBitmap();
				iconsPosToHihgestLayer();
			}
		}		
		
		/**
		 * @param e
		 */		
		protected function onMove(e:Event):void
		{
			var bm : Bitmap,
			s : Stage = StageManager.getInstance().stage;
			
			if( Math.abs( s.mouseX-_downPoint.x ) > ConfigManager.CAROUSEL_SENSE_NUM ) 
			{
				// 우선 현재 실제 화면을 제거하고,
				if( this.contains( childViewList[ selectedIndex ] ) ) this.removeChild( childViewList[ selectedIndex ] );
				
				// 움직임 true로 설정
				_onMove = true;
				
				_movePower = s.mouseX - _carouselPoint.x;
				
				// 현재화면에 대한 bitmap 을 새로운 것으로 갱신한다.
				if( _isNew == false )
				{
					if( childBMList[ 1 ] && this.contains( childBMList[ 1 ] as Bitmap ) )
					{
						this.removeChild( childBMList[ 1 ] );
						delete childBMList[ 1 ];
					}
					
					childBMList[ 1 ] = getChildBMAt( selectedIndex );
					addChild( childBMList[ 1 ] );
					
					// 페이지 아이콘 인덱스 갱신.
					iconsPosToHihgestLayer();
					
					_isNew = true;
				}
				
				
				// 마우스 무브에 따른 움직임을 시작한다.
				for each( bm in childBMList )
				{
					if( bm != null )
					{
						bm.x += _movePower; // 가로움직임.
					}
				}
				
				_carouselPoint.x = s.mouseX;
				_carouselPoint.y = s.mouseY;
			}
			
			
		}
		
		/**
		 * @param e
		 */		
		protected function onUp(e:Event):void
		{
			_onDown = false;
			
			var s : Stage = StageManager.getInstance().stage,
				currentChild : DisplayObject = childBMList[ 1 ];
			
			s.removeEventListener(MouseEvent.MOUSE_MOVE, onMove );
			s.removeEventListener(MouseEvent.MOUSE_UP, onUp );
//			s.removeEventListener(TouchEvent.TOUCH_MOVE, onMove );
//			s.removeEventListener(TouchEvent.TOUCH_END, onUp );
			
			// 만약 움직임이 없으면
			if( _onMove == false )
			{
				addChildCurrentView();
			}
			else
			{
				prevIndex = selectedIndex;
				
				// 움직인 거리를 판단해서 좌로 1칸 이동할지, 우로 1칸 이동할지 , 원위치할지 결정함. 
				if( currentChild.x + _movePower*10 < -currentChild.width*SENSITIVITY_FOR_FLICK 
					&& selectedIndex < childViewListRepository.length-1 )
				{
					selectedIndex++;
				}
				else if( currentChild.x + _movePower*10 > currentChild.width*SENSITIVITY_FOR_FLICK 
					&& selectedIndex > 0 )
				{
					selectedIndex--;
				}
				
				indexControl();
			}
			
		}
		
	}
}










